﻿// // ******************************************************************
// //       /\ /|       @file       RequestHelper.cs
// //       \ V/        @brief      await29
// //       | "")       @author     Await, 285283010@qq.com
// //       /  |
// //      /  \\        @Modified   2024-11-11 20:11
// //    *(__\_\        @Copyright  Copyright (c) , 2024-11-10 09:11 Await
// // ******************************************************************

using System.Collections;
using System.Net.Http.Headers;
using System.Text;
using System.Web;

namespace AutomaticSubtitles.Service.Infrastructure.Helper;

/// <summary>
///     请求帮助
/// </summary>
public static class RequestHelper
{
    /// <summary>
    /// 可以使用分组名称来请求数据
    /// </summary>
    /// <param name="httpClientFactory"></param>
    /// <param name="headers"></param>
    /// <param name="path"></param>
    /// <param name="parameter"></param>
    /// <param name="requestType"></param>
    /// <param name="key"></param>
    /// <typeparam name="TD"></typeparam>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    /// <exception cref="NullReferenceException"></exception>
    public static async Task<TD> GetServiceBasicData<TD>(this IHttpClientFactory httpClientFactory, Dictionary<string, string>? headers, string path, object? parameter = null,
        GlobalVariableEnumConfiguration.RequestTypeEnum requestType = GlobalVariableEnumConfiguration.RequestTypeEnum.get,
        string key = "")
    {
        try
        {
            using var httpClient = GetHttpClient(httpClientFactory, headers);
            Stream? networkGetRequest = null;
            switch (requestType)
            {
                case GlobalVariableEnumConfiguration.RequestTypeEnum.get:
                    var apiUrl = path;
                    if (parameter != null) apiUrl += ToQueryString(parameter, key);
                    networkGetRequest = await httpClient.GetStreamAsync(apiUrl);
                    // networkGetRequest = await httpClient.GetStringAsync(apiUrl);
                    break;
                case GlobalVariableEnumConfiguration.RequestTypeEnum.post:
                {
                    var content = httpClient.GetContent(parameter?.SerializeObject());
                    HttpResponseMessage postAsync;
                    if (content == null)
                        postAsync = await httpClient.PostAsync(path, null);
                    else
                        postAsync = await httpClient.PostAsync(path, content);
                    if (postAsync.IsSuccessStatusCode) networkGetRequest = await postAsync.Content.ReadAsStreamAsync();
                    break;
                }
                default:
                    throw new Exception("配置错误！");
            }

            if (networkGetRequest == null) throw new ArgumentException("请求失败");
            // var modelData = networkGetRequest.Deserialize<TD>();
            var modelData = networkGetRequest.DeserializeFromStream<TD>();
            if (modelData == null) throw new ArgumentException("未查询到数据");
            return modelData;
        }
        catch (Exception e)
        {
            throw new ArgumentException($"请求路径为{path},子服务调用失败！" + e.Message, e);
        }
    }

    private static HttpClient GetHttpClient(IHttpClientFactory client, Dictionary<string, string>? headers)
    {
        var httpClient = client.CreateClient();
        httpClient.Timeout = TimeSpan.FromSeconds(30);
        if (headers == null) return httpClient;
        foreach (var keyValuePair in headers)
            httpClient.DefaultRequestHeaders.Add(keyValuePair.Key, keyValuePair.Value);
        return httpClient;
    }

    /// <summary>
    /// Posts a set of data to a url, and deserializes the return stream into T
    /// </summary>
    /// <param name="client"></param>
    /// <param name="data"></param>
    /// <returns>Task{``0}.</returns>
    public static HttpContent? GetContent(this HttpClient client, string? data = "")
    {
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        client.DefaultRequestHeaders.ConnectionClose = false;
        if (data.IsNullOrWhiteSpace()) return default;
        var content = new StringContent(data, Encoding.UTF8, "application/json");
        return content;
    }

    /// <summary>
    ///     格式化Get请求参数
    /// </summary>
    /// <param name="url">URL</param>
    /// <param name="parameters">参数</param>
    /// <param name="paraEncoding">编码格式</param>
    /// <returns></returns>
    private static string FormatGetParametersToUrl(string url, Dictionary<string, string> parameters, Encoding paraEncoding)
    {
        if (url.IndexOf('?') < 0)
            url += "?";
        var i = 0;
        var sendContext = "";
        foreach (var parameter in parameters)
        {
            if (i > 0) sendContext += "&";

            sendContext += HttpUtility.UrlEncode(parameter.Key, paraEncoding)
                           + "=" + HttpUtility.UrlEncode(parameter.Value, paraEncoding);
            ++i;
        }

        url += sendContext;
        return url;
    }
    
    /// <summary>
    /// get请求参数转换
    /// </summary>
    /// <param name="obj"></param>
    /// <param name="key"></param>
    /// <param name="isFirstParam"></param>
    /// <returns></returns>
    private static string ToQueryString(object? obj, string key = "", bool isFirstParam = true)
    {
        var strBui = new StringBuilder();
        switch (obj)
        {
            case IEnumerable enumerable:
            {
                var index = 0;
                foreach (var item in enumerable)
                {
                    //判断是否是字符串
                    if (item is string str)
                    {
                        strBui.Append(isFirstParam ? '?' : '&');
                        strBui.Append($"{key}[{index}]={Uri.EscapeDataString(str)}");
                    }
                    else
                    {
                        foreach (var pro in item.GetType().GetProperties())
                        {
                            var value = pro.GetValue(item);
                            if (value == null)
                                continue;
                            var propertyName = string.IsNullOrWhiteSpace(key)
                                ? $"{pro.Name}[{index}]"
                                : $"{key}[{index}].{pro.Name}";

                            if (value is IEnumerable and not string)
                            {
                                strBui.Append(ToQueryString(value, propertyName, isFirstParam));
                            }
                            else if (value.GetType().IsClass && value.GetType() != typeof(string))
                            {
                                strBui.Append(ToQueryString(value, propertyName, isFirstParam));
                            }
                            else
                            {
                                strBui.Append(isFirstParam ? '?' : '&');
                                if (value is ValueType value1)
                                    strBui.Append($"{propertyName}={value1}");
                                else
                                    strBui.Append($"{propertyName}={Uri.EscapeDataString($"{value}")}");
                            }

                            isFirstParam = false;
                        }
                    }

                    isFirstParam = false;


                    index++;
                }

                break;
            }
            case ValueType:
                strBui.Append(isFirstParam ? '?' : '&');
                strBui.Append($"{key}={obj}");
                break;
            default:
            {
                //对象数据
                var proArray = obj?.GetType().GetProperties();
                if (proArray != null)
                    foreach (var pro in proArray)
                    {
                        var value = pro.GetValue(obj);
                        if (value == null)
                            continue;

                        var propertyName = string.IsNullOrWhiteSpace(key)
                            ? pro.Name
                            : $"{key}.{pro.Name}";

                        if (value is IEnumerable and not string)
                        {
                            strBui.Append(ToQueryString(value, propertyName, isFirstParam));
                        }
                        else if (value.GetType().IsClass && value.GetType() != typeof(string))
                        {
                            strBui.Append(ToQueryString(value, propertyName, isFirstParam));
                        }
                        else
                        {
                            strBui.Append(isFirstParam ? '?' : '&');
                            if (value is ValueType value1)
                                strBui.Append($"{propertyName}={value1}");
                            else
                                strBui.Append($"{propertyName}={Uri.EscapeDataString($"{value}")}");
                        }

                        isFirstParam = false;
                    }

                break;
            }
        }

        return strBui.ToString();
    }
}